package net.helix.graphics;

import java.nio.FloatBuffer;

import net.helix.core.transform.Transform;
import net.helix.core.transform.ViewTransform;
import net.helix.engine.Engine;
import net.helix.engine.viewPath.ViewPath;
import net.helix.ui.panels.view.ViewPreview;

import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.glu.GLU;

public class GraphicsEngine {
	private static int lastTextureBinded = -1;
	
	
	// TODO: make a geometry_light or something..
	static FloatBuffer fbP = BufferUtils.createFloatBuffer(4);
	static FloatBuffer fbA = BufferUtils.createFloatBuffer(4);
	static FloatBuffer fbD = BufferUtils.createFloatBuffer(4);
	static FloatBuffer fbS = BufferUtils.createFloatBuffer(4);
	static FloatBuffer fbSD = BufferUtils.createFloatBuffer(4);
	
	public static void rebindRequired(){
		lastTextureBinded = -1;
	}
	public static void bindTex(int toBind){
		if(lastTextureBinded != toBind){
			GL11.glBindTexture(GL11.GL_TEXTURE_2D, toBind);
		}
		lastTextureBinded = toBind;
	}
	public static void initGL(ViewPreview view){
		GL11.glCullFace(GL11.GL_BACK);
		GL11.glEnable(GL11.GL_CULL_FACE);
		
		fbP.put(1000);
		fbP.put(1300);
		fbP.put(-500);
		fbP.put(1);
		fbP.flip();
		GL11.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, fbP);
		
		
		fbA.put(.2f);
		fbA.put(.2f);
		fbA.put(.2f);
		fbA.put(1);
		fbA.flip();
		GL11.glLight(GL11.GL_LIGHT0, GL11.GL_AMBIENT, fbA);
		
		
		fbD.put(.8f);
		fbD.put(.8f);
		fbD.put(.8f);
		fbD.put(1);
		fbD.flip();
		GL11.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, fbD);
		
		fbS.put(1);
		fbS.put(1);
		fbS.put(1);
		fbS.put(1);
		fbS.flip();
		GL11.glLight(GL11.GL_LIGHT0, GL11.GL_SPECULAR, fbS);
		
		GL11.glLightf(GL11.GL_LIGHT0, GL11.GL_SPOT_CUTOFF, 180f);
		
		GL11.glLightf(GL11.GL_LIGHT0, GL11.GL_SPOT_EXPONENT, 0f);
		
		GL11.glViewport(0, 0, view.getWidth(), view.getHeight());
	}
	public static void switchRootFlat(Engine engine, ViewTransform viewTransform, ViewPath path, int width, int height, boolean director){
		// no depth testing in 2d mode!
		GL11.glDisable(GL11.GL_DEPTH_TEST);
		
		GL11.glMatrixMode(GL11.GL_PROJECTION);
		GL11.glLoadIdentity();
		
		if(director == false){ // this is the "Show All" type of zoom...
			float newWidth;
			float newHeight;
			
			// now to center everything...
			float aspectRatio = (float) engine.getProjectProperties().width / (float)engine.getProjectProperties().height;
			
			float r = aspectRatio / ((float) width / (float) height);
			if(r <= 1){
				newHeight = engine.getProjectProperties().height;
				newWidth = (int) ((newHeight*aspectRatio) / r);
	    	}else{
	    		newWidth = engine.getProjectProperties().width;
	    		newHeight = ((newWidth/aspectRatio) * r);
			}
			//
			GL11.glOrtho(-newWidth/2, newWidth/2, -newHeight/2, newHeight/2, 0, 1);
		}else{
			GL11.glOrtho(-width/2, width/2, -height/2, height/2, 0, 1);
		}
		
		if(viewTransform != null){
			// Here, viewTransform can apply  certain transformations scale and offset...
			viewTransform.getTransform().apply();
		}
		
		GL11.glMatrixMode(GL11.GL_MODELVIEW);
		
	}
	public static void switchRoot3D(Engine engine, ViewTransform viewTransform, ViewPath path, int width, int height, boolean director) {
		// enable depth testing for 3d mode (is disabled in 2d mode)
		GL11.glEnable(GL11.GL_DEPTH_TEST);
		GL11.glDepthFunc(GL11.GL_LEQUAL);
		// alpha blending will sure be complicated in 3d...
		
		GL11.glMatrixMode(GL11.GL_PROJECTION);
		GL11.glLoadIdentity();
		// make sure it sets a 3d projection...
		float newWidth;
		float newHeight;	
			
			// now to center everything...
			float aspectRatio = (float) engine.getProjectProperties().width / (float)engine.getProjectProperties().height;
			
			float r = aspectRatio / ((float) width / (float) height);
			if(r <= 1){
				newHeight = engine.getProjectProperties().height;
				newWidth = (int) ((newHeight*aspectRatio) / r);
	    	}else{
	    		newWidth = engine.getProjectProperties().width;
	    		newHeight = ((newWidth/aspectRatio) * r);
			}
			
			GLU.gluPerspective(45f, newWidth/newHeight, viewTransform.getNear(), viewTransform.getFar());
			
		// View's director's eye position will be controlled by the viewTransform's transformations...
		if(viewTransform != null){
			viewTransform.getTransform().apply();
		}
		
		GL11.glMatrixMode(GL11.GL_MODELVIEW);
		GL11.glLoadIdentity();
		
		if(path != null){
			if(path.getNumLights() > 0){
				// TODO: handle all of the lights
			}else{
				// default lights? (Maybe it should be 100% ambient...)
				fbP.put(-viewTransform.getTransform().getX());
				fbP.put(-viewTransform.getTransform().getY());
				fbP.put(-viewTransform.getTransform().getZ());
				fbP.put(1);
				fbP.flip();
				GL11.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, fbP);
			}
		}
		
		GraphicsHelper.setFrustrum(viewTransform.getTransform(), 45f, newWidth/newHeight, viewTransform.getNear(), viewTransform.getFar());
	}
}
